﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Diagnostics.Contracts;
using System.Resources;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using YasharEl.Infrastructure.ResourcesManagement.Configuration;

namespace YasharEl.Infrastructure.ResourcesManagement.Managers.Database
{
    /// <summary>
    /// Custom Resource Reader to read resources from a database
    /// </summary>
    [SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface")]
    [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
    public class DataResourceReader : IResourceReader
    {
        #region Fields

        DataProvider dataProvider;
        private string connectionString;
        private string baseName;
        private string cultureName;
        private bool useDataNodes;

        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets a value indicating whether to use data node values from the reader.
        /// </summary>
        /// <value><c>true</c> if the reader is to use data nodes; otherwise, <c>false</c>.</value>
        public bool UseDataNodes
        {
            get { return useDataNodes; }
            set { useDataNodes = value; }
        }
        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="T:DataResourceReader"/> class.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="baseName">the base name for the set of resources.</param>
        /// <param name="cultureInfo">The culture information.</param>
        public DataResourceReader(DataProvider dataProvider, string connectionString, string baseName, CultureInfo cultureInfo)
        {
            ValidateParameter(connectionString);

            this.dataProvider = dataProvider;
            this.connectionString = connectionString;

            if (baseName == null)
                this.baseName = String.Empty;
            else
                this.baseName = baseName;

            if (cultureInfo == null)
                this.cultureName = "neutral";
            else
                this.cultureName = (cultureInfo.Name.Length == 0) ? "neutral" : cultureInfo.Name;
        }

        private static void ValidateParameter(string connectionString)
        {
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(connectionString), "connectionString could not be null or empty.");
        }

        /// <summary>
        /// Use C# destructor syntax for finalization code.
        /// </summary>
        /// <remarks>
        /// This destructor will run only if the Dispose method
        /// does not get called.
        /// It gives your base class the opportunity to finalize.
        /// Do not provide destructors in types derived from this class.
        /// </remarks>
        ~DataResourceReader()
        {
            Dispose(false);
        }
        #endregion

        #region Public Methods

        /// <summary>
        /// Returns an <see cref="T:System.Collections.IDictionaryEnumerator"></see> of the resources for this reader.
        /// </summary>
        /// <returns>
        /// A dictionary enumerator for the resources for this reader.
        /// </returns>
        public IDictionaryEnumerator GetEnumerator()
        {
            Hashtable resources = null;
            IDataResourceReader resourceReader = null;

            // TODO: Refactor to factory method and oracle implementation in separated project
            switch (dataProvider)
            {
                case DataProvider.Oracle:
                    // TODO: Implement oracle data provider
                    throw new InvalidSystemConfigurationException("Oracle data provider not implemented yet.");
                    //break;
                case DataProvider.SqlServer:
                    resourceReader = new SqlServerDataResourceReader();
                    break;
                default:
                    throw new InvalidSystemConfigurationException(
                        string.Format("Could not recognize dataProvider : {0}", dataProvider)
                    );
            }
            if (resourceReader == null)
                throw new InvalidSystemConfigurationException("IDataResourceReader could not be null. Check configuration for data provider.");

            resources = resourceReader.PopulateResources(connectionString, baseName, cultureName);

            return resources.GetEnumerator();
        }

        /// <summary>
        /// Closes the resource reader after releasing any resources associated with it.
        /// </summary>
        public void Close()
        {
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        /// <summary>
        /// Releases all resources used by the <see cref="T:System.ComponentModel.Component"></see>.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Deserializes the value.
        /// </summary>
        /// <param name="value">The serialized value.</param>
        /// <returns>the deserialized value</returns>
        private static object DeserializeValue(object value)
        {
            if (value == DBNull.Value)
                value = null;
            else
            {
                byte[] valueBytes = (byte[])value;
                value = SerializationUtility.ToObject(valueBytes);
            }
            return value;
        }

        /// <summary>
        /// Releases the unmanaged resources used by the <see cref="T:System.ComponentModel.Component"></see> and optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Dispose of resources held by this instance.
                // (This process is not shown here.)
            }
        }
        #endregion
    }
}
